using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Diagnostics;
using System.Reflection;

namespace NAst.Common
{
    /// <summary>
    /// Represent a Network environment with its
    /// tests and commands
    /// </summary>
    public class NetworkConfiguration
    {
        private List<INetlet> myNetlets = new List<INetlet>();
        private List<INetworkTest> myTests = new List<INetworkTest>();
        private string _name;
        private bool _isConnected = false;

        public string Name
        {
            get { return _name; }
        }

        public NetworkConfiguration(string name)
        {
            _name = name;
        }

        /// <summary>
        /// Check if this NetworkConfiguration is active
        /// </summary>
        /// <returns>true if all tests have passed and the configuration
        /// is the active one</returns>
        internal bool Test()
        {
            foreach (INetworkTest t in myTests)
            {
                try
                {
                    if (!t.Test())
                        return false;
                }
                catch(Exception e)
                {
                    Trace.TraceError(e.Message);
                    System.Diagnostics.EventLog.WriteEntry("NetworkAsssitant", e.Message, System.Diagnostics.EventLogEntryType.Warning);
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Activate the current config
        /// </summary>
        /// <returns>A list of error raised when
        /// setting the user's environement</returns>
        /// <remarks>Each Netlet is executed in sequence</remarks>
        internal List<Exception> Connect()
        {
            List<Exception> ret = new List<Exception>();
            foreach (INetlet netlet in myNetlets)
            {
                try
                {
                    netlet.Connect();
                }
                catch(Exception e)
                {
                    ret.Add(e);
                }

            }
            _isConnected = true;

            return ret;
        }

        /// <summary>
        /// Deactivate the current configuration
        /// </summary>
        /// <returns>A list of exception encountered when de-activating</returns>
        internal List<Exception> Disconnect()
        {
            List<Exception> ret = new List<Exception>();
            foreach (INetlet netlet in myNetlets)
            {
                try
                {
                    netlet.Disconnect();
                }
                catch (Exception e)
                {
                    ret.Add(e);
                }

            }
            _isConnected = false;

            return ret;
        }


        internal void Initialize(XmlElement elmCurrentConfig, Dictionary<string, Type> allNetletsType, Dictionary<string, Type> allTestsType)
        {
            LoadTests(elmCurrentConfig, allTestsType);
            LoadNetlets(elmCurrentConfig, allNetletsType);
        }

        /// <summary>
        /// Load the Testers for this configuration
        /// </summary>
        /// <param name="elmCurrentConfig"></param>
        /// <param name="allTestType"></param>
        internal void LoadTests(XmlElement elmCurrentConfig, Dictionary<string, Type> allTestType)
        {
            foreach (XmlNode nd in elmCurrentConfig.SelectNodes("Tests/*"))
            {
                if (!(nd is XmlElement))
                    continue;
                XmlElement elm = nd as XmlElement;
                string testName = elm.LocalName;
                Type testType = null;
                if (!allTestType.TryGetValue(testName, out testType))
                {
                    Trace.TraceError("Test type " + testName + " not found!");
                    System.Diagnostics.EventLog.WriteEntry("NetworkAsssitant", "Test type " + testName + " not found!", System.Diagnostics.EventLogEntryType.Warning);
                    continue;
                }
                INetworkTest thisTest = null;
                ConstructorInfo ctr = testType.GetConstructor(Type.EmptyTypes);
                if (ctr == null)
                {
                    Trace.TraceError("No public constructor with no parameters for test " + testName);
                    System.Diagnostics.EventLog.WriteEntry("NetworkAsssitant", "No public constructor with no parameters for test " + testName, System.Diagnostics.EventLogEntryType.Warning);
                    continue;
                }
                thisTest = ctr.Invoke(new object[0]) as INetworkTest;
                thisTest.Initialize(_name, elm);
                myTests.Add(thisTest);
            }
        }

        /// <summary>
        /// Load the netlets (commands) for this configuration
        /// </summary>
        /// <param name="elmCurrentConfig"></param>
        /// <param name="allNetletsType"></param>
        internal void LoadNetlets(XmlElement elmCurrentConfig, Dictionary<string, Type> allNetletsType)
        {
            foreach (XmlNode nd in elmCurrentConfig.SelectNodes("Netlets/*"))
            {
                if (!(nd is XmlElement))
                    continue;
                XmlElement elm = nd as XmlElement;
                string netletName = elm.LocalName;
                INetlet thisNetlet = CreateNetlet(allNetletsType, netletName);
                if (thisNetlet == null)
                    continue;
                thisNetlet.Initialize(_name, elm);
                myNetlets.Add(thisNetlet);
            }
        }

        private static INetlet CreateNetlet(Dictionary<string, Type> allNetletsType, string netletName)
        {
            Type netletType = null;
            if (!allNetletsType.TryGetValue(netletName, out netletType))
            {
                Trace.TraceError("Netlet type " + netletName + " not found!");
                System.Diagnostics.EventLog.WriteEntry("NetworkAsssitant", "Netlet type " + netletName + " not found!", System.Diagnostics.EventLogEntryType.Warning);
                return null;
            }
            INetlet thisNetlet = null;
            ConstructorInfo ctr = netletType.GetConstructor(Type.EmptyTypes);
            if (ctr == null)
            {
                Trace.TraceError("No public constructor with no parameters for netlet " + netletName);
                System.Diagnostics.EventLog.WriteEntry("NetworkAsssitant", "No public constructor with no parameters for netlet " + netletName, System.Diagnostics.EventLogEntryType.Warning);
                return null;
            }
            thisNetlet = ctr.Invoke(new object[0]) as INetlet;
            return thisNetlet;
        }

        #region State persistence
        /// <summary>
        /// Save the configuration state to a xmldocument
        /// </summary>
        /// <param name="currState">XmlElement to save the state to</param>
        internal void Save(XmlElement currState)
        {
            if (!_isConnected) return;
            currState.SetAttribute("activeConfiguration", _name);
            foreach (INetlet netlet in myNetlets)
            {
                object[] atts = netlet.GetType().GetCustomAttributes(typeof(NetletAttribute), false);
                if (atts == null || atts.Length != 1)
                    continue;
                NetletAttribute att = atts[0] as NetletAttribute;
                XmlElement elmToSaveTo = currState.OwnerDocument.CreateElement(att.Name);
                currState.AppendChild(elmToSaveTo);
                netlet.SaveState(elmToSaveTo);
            }
        }

        /// <summary>
        /// Load a Network configuration from a saved-state 
        /// xml document
        /// </summary>
        /// <param name="currState"></param>
        /// <param name="allNetletsType"></param>
        /// <returns></returns>
        /// <remarks>The configuration loaded by this method will
        /// be set as the CurrentConfiguration on start-up. Its
        /// longivity is really small : after initializing,
        /// the NetworkAwareAssistant will replace it.</remarks>
        internal static NetworkConfiguration Load(XmlElement currState, Dictionary<string, Type> allNetletsType)
        {
            string configName = currState.GetAttribute("activeConfiguration");
            if(string.IsNullOrEmpty(configName))
                return null;

            NetworkConfiguration retVal = new NetworkConfiguration(configName);
            foreach (XmlNode nd in currState.SelectNodes("./*"))
            {
                if (!(nd is XmlElement))
                    continue;
                XmlElement elm = nd as XmlElement;
                string netletName = elm.LocalName;
                INetlet thisNetlet = CreateNetlet(allNetletsType, netletName);
                if (thisNetlet == null)
                    continue;
                thisNetlet.LoadState(elm);
                retVal.myNetlets.Add(thisNetlet);
            }
            return retVal;
        }
        #endregion
    }
}
